import type { Metadata } from 'next'; import { ScrollX } from '@/components/models/scroll-x'; import Link from 'next/link'; import { notFound } from 'next/navigation'; import type { ScatterPoint } from '@/components/charts'; import { ParetoChart } from '@/components/benchmarks/client-charts'; import { TrustBadge } from '@/components/models/badges'; import { configChipsOf, fmtScoreUnit, opennessLabel, xFormatter } from '@/components/models/shared'; import { Estimated } from '@/components/ui/badges'; import { DataTable, Td, Th } from '@/components/ui/data-table'; import { Container, Note, PageHeader } from '@/components/ui/section'; import { EmptyState, Unavailable } from '@/components/ui/unavailable'; import { ApiError, apiD1, safe } from '@/lib/api'; import { cn } from '@/lib/cn'; import { fmtInt } from '@/lib/format'; import { routes, SITE_NAME, SITE_URL } from '@/lib/site'; import type { ParetoPayload } from '@/lib/types'; /* Cost vs performance for one benchmark (Pareto view from `/pareto`): X = cheapest current output price (log toggle) or another axis, Y = score in the comparability group, bubble = context or parameters (joined from a second /pareto call), frontier from the API. */ type SP = Record; type Params = { params: Promise<{ slug: string }>; searchParams: Promise }; export const revalidate = 600; const X_AXES = [ { key: 'output_price', label: 'Output price' }, { key: 'input_price', label: 'Input price' }, { key: 'parameter_count', label: 'Parameters' }, { key: 'context_length', label: 'Context' }, { key: 'memory_estimate', label: 'Memory (est.)' }, ]; export async function generateMetadata({ params }: Params): Promise { const { slug } = await params; const d = await safe(apiD1.benchmark(slug)); if (!d || d.entity_type !== 'benchmark') return { title: 'Cost vs performance', robots: { index: false } }; const title = `${d.name} — Cost vs Performance (Pareto)`; const description = `Every canonical model with a current ${d.name} result plotted against its cheapest current output price (USD per 1M tokens), with the Pareto frontier — same comparability group only, trust level on every point. ${SITE_NAME}.`; const canonical = `${routes.benchmark(d.slug)}/cost-vs-performance`; return { title, description, alternates: { canonical }, openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${canonical}`, type: 'article' } }; } async function loadPareto(q: Record): Promise<{ res: ParetoPayload | null; error: string | null }> { try { return { res: await apiD1.pareto(q), error: null }; } catch (e) { if (e instanceof ApiError && e.notFound) notFound(); if (e instanceof ApiError && (e.status === 400 || e.status === 422)) return { res: null, error: e.detail ?? 'Unsupported axis.' }; return { res: null, error: null }; } } export default async function CostVsPerformancePage({ params, searchParams }: Params) { const { slug } = await params; const sp = await searchParams; const x = X_AXES.find((a) => a.key === sp.x) ?? X_AXES[0]!; const log = sp.log !== '0'; const bubble = sp.bubble === 'params' ? 'params' : sp.bubble === 'none' ? 'none' : 'context'; const metric = sp.metric?.trim() || undefined; const configKey = sp.config_key?.trim() || undefined; const org = sp.org?.trim() || undefined; const openness = sp.openness?.trim() || undefined; const base = { benchmark: slug, metric, config_key: configKey, org, openness }; const [detail, { res, error }, bubbleRes] = await Promise.all([safe(apiD1.benchmark(slug)), loadPareto({ ...base, x: x.key }), bubble === 'none' ? Promise.resolve(null) : safe(apiD1.pareto({ ...base, x: bubble === 'params' ? 'parameter_count' : 'context_length' }))]); if (!detail || detail.entity_type !== 'benchmark') notFound(); const canonical = `${routes.benchmark(detail.slug)}/cost-vs-performance`; const href = (patch: SP) => { const p = new URLSearchParams(); for (const [k, v] of Object.entries({ x: x.key === 'output_price' ? undefined : x.key, log: log ? undefined : '0', bubble: bubble === 'context' ? undefined : bubble, metric, config_key: configKey, org, openness, ...patch })) if (v) p.set(k, v); const s = p.toString(); return `${canonical}${s ? `?${s}` : ''}`; }; const unit = typeof detail.attributes?.unit === 'string' ? (detail.attributes.unit as string) : null; const bubbleById = new Map(); for (const p of bubbleRes?.points ?? []) bubbleById.set(p.model.id, p.x); const frontierSet = new Set(res?.frontier ?? []); const points: ScatterPoint[] = (res?.points ?? []).map((p) => { const chips = configChipsOf(p.config, res?.group?.config ?? null, 3); return { id: p.id, x: p.x, y: p.y, r: bubble === 'none' ? 1 : bubbleById.get(p.model.id) ?? 1, label: p.model.name, sub: [p.model.organization, p.provider?.name ? `via ${p.provider.name}` : null, `rank ${p.rank}`, p.trust_level, chips.map((c) => `${c.key}=${c.value}`).join(' ') || null, p.estimated ? 'estimated' : null].filter(Boolean).join(' · '), href: `/models/${encodeURIComponent(p.model.slug)}`, color: p.model.openness && /open|restricted/.test(p.model.openness) ? 'var(--positive)' : 'var(--type-model)', group: p.model.openness ?? undefined, }; }); const frontierPts = points.filter((p) => frontierSet.has(p.id)).sort((a, b) => a.x - b.x); const hib = res?.group?.higher_is_better !== false; const yFmt = (v: number) => fmtScoreUnit(v, unit); const xFmt = xFormatter(x.key); const chip = (on: boolean) => cn('inline-flex h-8 items-center border px-2.5 text-xs whitespace-nowrap', on ? 'border-ink bg-ink text-canvas' : 'border-rule text-ink-2 hover:border-rule-strong hover:text-ink'); const ld = { '@context': 'https://schema.org', '@type': 'Dataset', name: `${detail.name} — cost vs performance`, url: `${SITE_URL}${canonical}`, description: res?.methodology }; return (